package cn.example.util.toolkit;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 对象工具类
 * Created by WuYubin on 2016/12/28.
 */
public class BeanUtil {

    private static final Logger log = LoggerFactory.getLogger(BeanUtil.class);

    private static final Validator validator = Validation
            .byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();

    private static class SingletonHolder {
        private static final BeanUtil INSTANCE = new BeanUtil();
    }

    private BeanUtil() {
    }

    public static BeanUtil getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 父类对象转子类
     *
     * @param parent 父类对象
     * @param clazz  子类Class
     * @return <E extends T> E
     */
    public <E extends T, T> E parentToChild(T parent, Class<E> clazz) {
        String jsonStr = JSON.toJSONString(parent);
        return JSON.parseObject(jsonStr, clazz);
    }

    /**
     * 校验框架检验对象
     *
     * @param bean 待校验对象
     * @return int
     */
    public <T> int validate(T bean) {
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(bean);
        return constraintViolations.size();
    }


    /**
     * 获得对象，NULL时返回默认对象
     *
     * @param bean 要获得的对象
     * @param def  默认对象
     * @return <T> T
     */
    public <T> T getNonNullBean(T bean, T def) {
        return Optional
                .ofNullable(bean)
                .orElse(def);
    }

    /**
     * 转换对象为XML字符串
     *
     * @param t 转换对象
     * @return String
     */
    public <T> String obj2xml(T t) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(t.getClass());
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty("jaxb.encoding", "UTF-8");
            marshaller.setProperty("jaxb.fragment", Boolean.TRUE);
            StringWriter sw = new StringWriter();
            marshaller.marshal(t, sw);
            StringBuffer sb = sw.getBuffer();
            sw.close();
            if (sb != null) {
                return sb.toString();
            }
            return null;
        } catch (Exception e) {
            log.error("转换对象为XML异常", e);
            return null;
        }
    }

    /**
     * 转换XML字符串为对象
     *
     * @param clazz 类的Class对象
     * @param xml   xml字符串
     * @return <T> T
     */
    public <T> T xml2Obj(Class<T> clazz, String xml) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            Object obj = unmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes("UTF-8")));
            if (clazz.isInstance(obj)) {
                return clazz.cast(obj);
            }
            return null;
        } catch (Exception e) {
            log.error("转换XML为对象异常", e);
            return null;
        }
    }

    /**
     * 转换对象为Map
     *
     * @param obj 转换对象
     * @return Map
     */
    public Map<String, Object> obj2Map(Object obj) {
        String json = JSON.toJSONString(obj);
        return JSON.parseObject(json, new TypeReference<Map<String, Object>>() {
        });
    }

    /**
     * 转换Map为对象
     *
     * @param map   Map
     * @param clazz 类的Class对象
     * @return <T> T
     */
    public <T> T map2Obj(Map<String, Object> map, Class<T> clazz) {
        String jsonStr = JSON.toJSONString(map);
        return JSON.parseObject(jsonStr, clazz);
    }

    /**
     * 获取类泛型类型
     *
     * @param clazz 当前类型
     * @return Class
     */
    public Class getGenericClass(Class<?> clazz) {
        ParameterizedType t = (ParameterizedType) clazz.getGenericSuperclass();
        Type generic = t.getActualTypeArguments()[0];
        if (generic instanceof Class) {
            return (Class) generic;
        }
        return null;
    }

    /**
     * 获得类注解
     *
     * @param annotationClass 注解类型
     * @param clazz           被注解的类
     * @return <T extends Annotation> T
     */
    public <T extends Annotation> T getTypeAnnotation(Class<T> annotationClass, Class clazz) {
        Annotation obj = clazz.getAnnotation(annotationClass);
        if (annotationClass.isInstance(obj)) {
            return annotationClass.cast(obj);
        }
        return null;
    }

    /**
     * 获得字段注解
     *
     * @param field           字段
     * @param annotationClass 注解类型
     * @return <T extends Annotation> T
     */
    public <T extends Annotation> T getFieldAnnotation(Field field, Class<T> annotationClass) {
        if (isAnnotationField(field, annotationClass)) {
            Annotation obj = field.getAnnotation(annotationClass);
            if (annotationClass.isInstance(obj)) {
                return annotationClass.cast(obj);
            }
        }
        return null;
    }

    /**
     * 获得注解的字段集合
     *
     * @param clazz           类型
     * @param annotationClass 注解类型
     * @return <T extends Annotation> T
     */
    public List<Field> getAnnotationFields(Class clazz, Class<? extends Annotation> annotationClass) {
        Field[] fields = clazz.getDeclaredFields();
        List<Field> list = new ArrayList<>();
        for (Field field : fields) {
            if (isAnnotationField(field, annotationClass)) {
                list.add(field);
            }
        }
        return list;
    }

    /**
     * 判断字段是否有注解
     *
     * @param field           字段
     * @param annotationClass 注解类型
     * @return boolean
     */
    public boolean isAnnotationField(Field field, Class<? extends Annotation> annotationClass) {
        return field != null && annotationClass != null && field.isAnnotationPresent(annotationClass);
    }

    /**
     * 判断字段是否是静态/只读字段
     *
     * @param field 字段
     * @return boolean
     */
    public boolean isStaticOrFinal(Field field) {
        int mod = field.getModifiers();
        return Modifier.isStatic(mod) || Modifier.isFinal(mod);
    }

}
